﻿#ifndef QTZ_COMMON_INCLUDED
#define QTZ_COMMON_INCLUDED

#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
#include "UnityInstancing.cginc"
#include "UnityPBSLighting.cginc"
#include "UnityStandardBRDF.cginc"

// SH lighting environment
half4 qtz_SHAr;
half4 qtz_SHAg;
half4 qtz_SHAb;
half4 qtz_SHBr;
half4 qtz_SHBg;
half4 qtz_SHBb;
half4 qtz_SHC;

UNITY_DECLARE_TEXCUBE(qtz_SpecCube0);
#if defined(SHADER_API_MOBILE)
half4 qtz_SpecCube0_HDR = half4(2, 1, 0, 0);
half4 qtz_SpecCube1_HDR = half4(2, 1, 0, 0);
#else
half4 qtz_SpecCube0_HDR = half4(1, 1, 0, 0);
half4 qtz_SpecCube1_HDR = half4(1, 1, 0, 0);
#endif

half4 qtz_SpecColor;
half qtz_SpecRotation;
half qtz_SpecExposure;

//统一 GI!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
#if defined(_CUSTOM_ENV_ON) || defined(_CUSTOM_ENV_VIEW_ON)

#define SHAr qtz_SHAr
#define SHAg qtz_SHAg
#define SHAb qtz_SHAb
#define SHBr qtz_SHBr
#define SHBg qtz_SHBg
#define SHBb qtz_SHBb
#define SHC qtz_SHC

#define SpecCube0 qtz_SpecCube0
#define SpecCube1 

#define SpecCube0_HDR qtz_SpecCube0_HDR
#define SpecCube1_HDR qtz_SpecCube1_HDR

#else

#define SHAr unity_SHAr
#define SHAg unity_SHAg
#define SHAb unity_SHAb
#define SHBr unity_SHBr
#define SHBg unity_SHBg
#define SHBb unity_SHBb
#define SHC unity_SHC

#define SpecCube0 unity_SpecCube0
#define SpecCube1 unity_SpecCube1

#define SpecCube0_HDR unity_SpecCube0_HDR
#define SpecCube1_HDR unity_SpecCube1_HDR

#endif

struct IndirectLight
{
	half3 diffuse;
	half3 specular;
};

inline half3 RotateAroundYInDegrees(half3 dir, half degrees)
{
	half theta = degrees * UNITY_PI / 180.0f;
	half costha = cos(theta);
	half sintha = sin(theta);
	half x = dir.x * costha - dir.z * sintha;
	half z = dir.x * sintha + dir.z * costha;
	return half3(x, dir.y, z);
}

half3 QtzSHEvalLinearL0L1(half4 normal)
{
	half3 x;

	// Linear (L1) + constant (L0) polynomial terms
	x.r = dot(SHAr, normal);
	x.g = dot(SHAg, normal);
	x.b = dot(SHAb, normal);

	return x;
}

half3 QtzSHEvalLinearL2(half4 normal)
{
	half3 x1, x2;
	// 4 of the quadratic (L2) polynomials
	half4 vB = normal.xyzz * normal.yzzx;
	x1.r = dot(SHBr, vB);
	x1.g = dot(SHBg, vB);
	x1.b = dot(SHBb, vB);

	// Final (5th) quadratic (L2) polynomial
	half vC = normal.x*normal.x - normal.y*normal.y;
	x2 = SHC.rgb * vC;

	return x1 + x2;
}

// normal should be normalized, w=1.0
// output in active color space
half3 QtzShadeSH9(half4 normal)
{
	// Linear + constant polynomial terms
	half3 res = QtzSHEvalLinearL0L1(normal);

	// Quadratic polynomials
	res += QtzSHEvalLinearL2(normal);

//#ifdef UNITY_COLORSPACE_GAMMA
//	res = LinearToGammaSpace(res);
//#endif

	return res;
}

half3 QtzShadeSHPerVertex(half3 normal, half3 ambient)
{
#if UNITY_SAMPLE_FULL_SH_PER_PIXEL
	// Completely per-pixel
	// nothing to do here
#elif (SHADER_TARGET < 30) || UNITY_STANDARD_SIMPLE
	// Completely per-vertex
	ambient += max(half3(0, 0, 0), QtzShadeSH9(half4(normal, 1.0)));
#else
	// L2 per-vertex, L0..L1 & gamma-correction per-pixel

	// NOTE: SH data is always in Linear AND calculation is split between vertex & pixel
	// Convert ambient to Linear and do final gamma-correction at the end (per-pixel)
//#ifdef UNITY_COLORSPACE_GAMMA
//	ambient = GammaToLinearSpace(ambient);
//#endif
	ambient += QtzSHEvalLinearL2(half4(normal, 1.0));     // no max since this is only L2 contribution
#endif

	return ambient;
}

half3 QtzShadeSHPerPixel(half3 normal, half3 ambient, float3 worldPos)
{
	half3 ambient_contrib = 0.0;

#if UNITY_SAMPLE_FULL_SH_PER_PIXEL
	// Completely per-pixel
	ambient_contrib = QtzShadeSH9(half4(normal, 1.0));
	ambient += max(half3(0, 0, 0), ambient_contrib);
#elif (SHADER_TARGET < 30) || UNITY_STANDARD_SIMPLE
	// Completely per-vertex
	// nothing to do here
#else
	// L2 per-vertex, L0..L1 & gamma-correction per-pixel
	// Ambient in this case is expected to be always Linear, see ShadeSHPerVertex()
#if UNITY_LIGHT_PROBE_PROXY_VOLUME
	if (unity_ProbeVolumeParams.x == 1.0)
		ambient_contrib = SHEvalLinearL0L1_SampleProbeVolume(half4(normal, 1.0), worldPos);
	else
		ambient_contrib = QtzSHEvalLinearL0L1(half4(normal, 1.0));
#else
	ambient_contrib = QtzSHEvalLinearL0L1(half4(normal, 1.0));
#endif

	ambient = max(half3(0, 0, 0), ambient + ambient_contrib);     // include L2 contribution in vertex shader before clamp.
//#ifdef UNITY_COLORSPACE_GAMMA
//	ambient = LinearToGammaSpace(ambient);
//#endif
#endif
	return ambient;
}

inline UnityGI QtzGI_Base(UnityGIInput data, inout half occlusion, half3 normalWorld, half3 env)
{
	UnityGI o_gi;
	ResetUnityGI(o_gi);

	// Base pass with Lightmap support is responsible for handling ShadowMask / blending here for performance reason
#if defined(HANDLE_SHADOWS_BLENDING_IN_GI)
	half bakedAtten = UnitySampleBakedOcclusion(data.lightmapUV.xy, data.worldPos);
	float zDist = dot(_WorldSpaceCameraPos - data.worldPos, UNITY_MATRIX_V[2].xyz);
	float fadeDist = UnityComputeShadowFadeDistance(data.worldPos, zDist);
	data.atten = UnityMixRealtimeAndBakedShadows(data.atten, bakedAtten, UnityComputeShadowFade(fadeDist));
#endif

	o_gi.light = data.light;
	o_gi.light.color *= data.atten;

#if UNITY_SHOULD_SAMPLE_SH
	o_gi.indirect.diffuse = QtzShadeSHPerPixel(normalWorld, data.ambient, data.worldPos) * env;
#endif

#if defined(LIGHTMAP_ON)
	// Baked lightmaps
	half4 bakedColorTex = UNITY_SAMPLE_TEX2D(unity_Lightmap, data.lightmapUV.xy);
	half3 bakedColor = DecodeLightmap(bakedColorTex);

	#ifdef DIRLIGHTMAP_COMBINED
		fixed4 bakedDirTex = UNITY_SAMPLE_TEX2D_SAMPLER(unity_LightmapInd, unity_Lightmap, data.lightmapUV.xy);
		o_gi.indirect.diffuse = DecodeDirectionalLightmap(bakedColor, bakedDirTex, normalWorld);
		#if defined(LIGHTMAP_SHADOW_MIXING) && !defined(SHADOWS_SHADOWMASK) && defined(SHADOWS_SCREEN)
			ResetUnityLight(o_gi.light);
			o_gi.indirect.diffuse = SubtractMainLightWithRealtimeAttenuationFromLightmap(o_gi.indirect.diffuse, data.atten, bakedColorTex, normalWorld);
		#endif
	#else // not directional lightmap
		o_gi.indirect.diffuse = bakedColor;

		#if defined(LIGHTMAP_SHADOW_MIXING) && !defined(SHADOWS_SHADOWMASK) && defined(SHADOWS_SCREEN)
			ResetUnityLight(o_gi.light);
			o_gi.indirect.diffuse = SubtractMainLightWithRealtimeAttenuationFromLightmap(o_gi.indirect.diffuse, data.atten, bakedColorTex, normalWorld);
		#endif

	#endif
#if defined(UNITY_COLORSPACE_GAMMA)
	o_gi.indirect.diffuse = GammaToLinearSpace(o_gi.indirect.diffuse);
#endif
#endif

#ifdef DYNAMICLIGHTMAP_ON
	// Dynamic lightmaps
	half3 dynamicLightmapColor = half3(0, 0, 0);
	fixed4 realtimeColorTex = UNITY_SAMPLE_TEX2D(unity_DynamicLightmap, data.lightmapUV.zw);
	half3 realtimeColor = DecodeRealtimeLightmap(realtimeColorTex);

	#ifdef DIRLIGHTMAP_COMBINED
		half4 realtimeDirTex = UNITY_SAMPLE_TEX2D_SAMPLER(unity_DynamicDirectionality, unity_DynamicLightmap, data.lightmapUV.zw);
		dynamicLightmapColor += DecodeDirectionalLightmap(realtimeColor, realtimeDirTex, normalWorld);
	#else
		dynamicLightmapColor += realtimeColor;
	#endif
	#if defined(UNITY_COLORSPACE_GAMMA)
		dynamicLightmapColor = GammaToLinearSpace(dynamicLightmapColor);
	#endif
	o_gi.indirect.diffuse += dynamicLightmapColor;
#endif
	o_gi.indirect.diffuse *= occlusion;
	return o_gi;
}

// Decodes HDR textures
// handles dLDR, RGBM formats
inline half3 QtzDecodeHDR (half4 data, half4 decodeInstructions)
{
    // Take into account texture alpha if decodeInstructions.w is true(the alpha value affects the RGB channels)
    half alpha = decodeInstructions.w * (data.a - 1.0) + 1.0;

    // If Linear mode is not supported we can skip exponent part
    #if defined(UNITY_COLORSPACE_GAMMA)
        return (decodeInstructions.x * alpha) * data.rgb;
    #else
    #   if defined(UNITY_USE_NATIVE_HDR)
            return decodeInstructions.x * data.rgb; // Multiplier for future HDRI relative to absolute conversion.
    #   else
            return (decodeInstructions.x * pow(alpha, decodeInstructions.y)) * data.rgb;
    #   endif
    #endif
}

half3 Qtz_GlossyEnvironment(UNITY_ARGS_TEXCUBE(tex), half4 hdr, Unity_GlossyEnvironmentData glossIn)
{
	half perceptualRoughness = glossIn.roughness /* perceptualRoughness */;

	// TODO: CAUTION: remap from Morten may work only with offline convolution, see impact with runtime convolution!
	// For now disabled
#if 0
	float m = PerceptualRoughnessToRoughness(perceptualRoughness); // m is the real roughness parameter
	const float fEps = 1.192092896e-07F;        // smallest such that 1.0+FLT_EPSILON != 1.0  (+1e-4h is NOT good here. is visibly very wrong)
	float n = (2.0 / max(fEps, m*m)) - 2.0;        // remap to spec power. See eq. 21 in --> https://dl.dropboxusercontent.com/u/55891920/papers/mm_brdf.pdf

	n /= 4;                                     // remap from n_dot_h formulatino to n_dot_r. See section "Pre-convolved Cube Maps vs Path Tracers" --> https://s3.amazonaws.com/docs.knaldtech.com/knald/1.0.0/lys_power_drops.html

	perceptualRoughness = pow(2 / (n + 2), 0.25);      // remap back to square root of real roughness (0.25 include both the sqrt root of the conversion and sqrt for going from roughness to perceptualRoughness)
#else
	// MM: came up with a surprisingly close approximation to what the #if 0'ed out code above does.
	perceptualRoughness = perceptualRoughness*(1.7 - 0.7*perceptualRoughness);
#endif


	half mip = perceptualRoughnessToMipmapLevel(perceptualRoughness);
	half3 R = glossIn.reflUVW;
	half4 rgbm = UNITY_SAMPLE_TEXCUBE_LOD(tex, R, mip);

	half3 hdrColor = QtzDecodeHDR(rgbm, hdr);
#ifdef UNITY_COLORSPACE_GAMMA
	hdrColor = GammaToLinearSpace(hdrColor);
#endif
	return hdrColor;
}


inline half3 QtzGI_IndirectSpecular(UnityGIInput data, half occlusion, Unity_GlossyEnvironmentData glossIn)
{
	half3 specular;

#ifdef UNITY_SPECCUBE_BOX_PROJECTION
	// we will tweak reflUVW in glossIn directly (as we pass it to Unity_GlossyEnvironment twice for probe0 and probe1), so keep original to pass into BoxProjectedCubemapDirection
	half3 originalReflUVW = glossIn.reflUVW;
	glossIn.reflUVW = BoxProjectedCubemapDirection(originalReflUVW, data.worldPos, data.probePosition[0], data.boxMin[0], data.boxMax[0]);
#endif

#ifdef _GLOSSYREFLECTIONS_OFF
	specular = unity_IndirectSpecColor.rgb;
#else
	half3 env0 = Qtz_GlossyEnvironment(UNITY_PASS_TEXCUBE(SpecCube0), data.probeHDR[0], glossIn);
#if defined(UNITY_SPECCUBE_BLENDING) && !defined(_CUSTOM_ENV_ON) && !defined(_CUSTOM_ENV_VIEW_ON)
	const float kBlendFactor = 0.99999;
	float blendLerp = data.boxMin[0].w;
	UNITY_BRANCH
		if (blendLerp < kBlendFactor)
		{
#ifdef UNITY_SPECCUBE_BOX_PROJECTION
			glossIn.reflUVW = BoxProjectedCubemapDirection(originalReflUVW, data.worldPos, data.probePosition[1], data.boxMin[1], data.boxMax[1]);
#endif

			half3 env1 = Qtz_GlossyEnvironment(UNITY_PASS_TEXCUBE_SAMPLER(SpecCube1, SpecCube0), data.probeHDR[1], glossIn);
			specular = lerp(env1, env0, blendLerp);
		}
		else
		{
			specular = env0;
		}
#else
	specular = env0;
#endif
#endif
	return specular * occlusion;
}

inline UnityGI QtzGlobalIllumination(UnityGIInput data, half occlusion, half3 normalWorld, Unity_GlossyEnvironmentData glossIn)
{
#if defined(_CUSTOM_ENV_ON) || defined(_CUSTOM_ENV_VIEW_ON)
	half3 env = pow(2.2, qtz_SpecExposure * 0.45f) * unity_ColorSpaceDouble.rgb * qtz_SpecColor.rgb;
#else
	half3 env = half3(1, 1, 1);
#endif

	UnityGI o_gi = QtzGI_Base(data, occlusion, normalWorld, env);
	o_gi.indirect.specular = QtzGI_IndirectSpecular(data, occlusion, glossIn) * env;
	return o_gi;
}

inline half3 QtzDiffuseAndSpecularFromMetallic(half3 albedo, half metallic, out half3 specColor, out half oneMinusReflectivity)
{
#ifdef UNITY_COLORSPACE_GAMMA
	albedo = GammaToLinearSpace(albedo);
	half4 ColorSpaceDielectricSpec = half4(0.04, 0.04, 0.04, 1.0 - 0.04);
	specColor = lerp(ColorSpaceDielectricSpec.rgb, albedo, metallic);
	half oneMinusDielectricSpec = ColorSpaceDielectricSpec.a;
	oneMinusReflectivity = oneMinusDielectricSpec - metallic * oneMinusDielectricSpec;
	return albedo * oneMinusReflectivity;
#else
	specColor = lerp(unity_ColorSpaceDielectricSpec.rgb, albedo, metallic);
	oneMinusReflectivity = OneMinusReflectivityFromMetallic(metallic);
	return albedo * oneMinusReflectivity;
#endif
}

inline half3 QtzRotationGIDirection(half3 normalDirection)
{
	half3 normalWorld = normalDirection;
#if defined(_CUSTOM_ENV_VIEW_ON)
	half3x3 viewSpaceIT = //transpose(UNITY_MATRIX_V);
		half3x3(
			half3(UNITY_MATRIX_V[0].x, UNITY_MATRIX_V[1].x, -UNITY_MATRIX_V[2].x),
			half3(UNITY_MATRIX_V[0].y, UNITY_MATRIX_V[1].y, -UNITY_MATRIX_V[2].y),
			half3(UNITY_MATRIX_V[0].z, UNITY_MATRIX_V[1].z, -UNITY_MATRIX_V[2].z)
			);
	normalWorld = mul(normalWorld, viewSpaceIT);
#endif

	half theta = (270 - qtz_SpecRotation) * UNITY_PI / 180.0f;
	half costha = cos(theta);
	half sintha = sin(theta);
	normalWorld = half3(normalWorld.x * costha - normalWorld.z * sintha, normalWorld.y, normalWorld.x * sintha + normalWorld.z * costha);
	return normalWorld;
}

inline half3 QtzRotationGIDirection(half3 normalDirection, half3 viewDirection, out half3 reflUVW)
{
	half3 normalWorld = normalDirection;
	reflUVW = reflect(-viewDirection, normalDirection);
#if defined(_CUSTOM_ENV_VIEW_ON)
	half3x3 viewSpaceIT = //transpose(UNITY_MATRIX_V);
		half3x3(
			half3(UNITY_MATRIX_V[0].x, UNITY_MATRIX_V[1].x, -UNITY_MATRIX_V[2].x),
			half3(UNITY_MATRIX_V[0].y, UNITY_MATRIX_V[1].y, -UNITY_MATRIX_V[2].y),
			half3(UNITY_MATRIX_V[0].z, UNITY_MATRIX_V[1].z, -UNITY_MATRIX_V[2].z)
			);
	normalWorld = mul(normalWorld, viewSpaceIT);
	reflUVW = mul(reflUVW, viewSpaceIT);
#endif

	half theta = (270 - qtz_SpecRotation) * UNITY_PI / 180.0f;
	half costha = cos(theta);
	half sintha = sin(theta);
	normalWorld = half3(normalWorld.x * costha - normalWorld.z * sintha, normalWorld.y, normalWorld.x * sintha + normalWorld.z * costha);
	reflUVW = half3(reflUVW.x * costha - reflUVW.z * sintha, reflUVW.y, reflUVW.x * sintha + reflUVW.z * costha);
	return normalWorld;
}

/////////////////////////////////////////Direct Lighting/////////////////////////////////////////////////
//来自于Unity内置的BRDF
inline half3 SpecularBRDF(half NdotH, half NdotV, half NdotL, half VdotH, half roughness, half3 f0)
{
	//微表面BRDF公式
	//                D(h) F(v,h) G(l,v,h)
	//f(l,v) = ---------------------------
	//                   4(n·l)(n·v)
	half a2 = roughness * roughness;
	//UnityStandardDRDF.cginc GGXTerm (NDF) 可替换成BlinnPhong.
	half d = (NdotH * a2 - NdotH) * NdotH + 1.0f; // 2 mad
	half D = a2 / (d * d + 1e-7f);

	//UnityStandardDRDF.cginc SmithJointGGXVisibilityTerm 
	half lambdaV = NdotL * (NdotV * (1 - roughness) + roughness);
	half lambdaL = NdotV * (NdotL * (1 - roughness) + roughness);
	half G = 0.5f / (lambdaV + lambdaL + 1e-5f);

	half specularTerm = D * G;

//#ifdef UNITY_COLORSPACE_GAMMA //G与D进行伽马矫正,菲涅没有加入Gamma运算
//	specularTerm = sqrt(max(1e-4h, specularTerm));
//#endif
	specularTerm = max(0, specularTerm * NdotL);

	//float F = f0 + (1 - f0) * pow(2, (-5.55473 * VdotH - 6.98316) * VdotH);//参数是从UE4那里抄来的，应该是Schlick公式的趋近
	half3 F = f0 + (1 - f0) * Pow5(1 - VdotH);
	return F * specularTerm;//加入菲涅尔运算
}

inline half3 AnisotropicSpecularBRDF(half NdotH, half NdotV, half NdotL, half VdotH, half TdotH, half BdotH, half roughnessX, half roughnessY, half3 f0)
{
	half term0 = 1 / sqrt(NdotL * NdotV);
	half term1 = NdotL / (4 * UNITY_PI * roughnessX * roughnessY);
	half x = TdotH / roughnessX;
	half y = BdotH / roughnessY;
	half term2 = exp(-2 * (x * x + y * y) / 1 + NdotH);
	half specularTerm = term0 * term1 * term2;

	half3 F = f0 + (1 - f0) * Pow5(1 - VdotH);
	return F * specularTerm;//加入菲涅尔运算
}

//OrenNayar漫反射模型,可替换成Lambert
inline half3 OrenNayarDiffuse(half3 LdotH, half3 NdotL, half3 NdotV, half perceptualRoughness)
{
	half fd90 = 0.5 + 2 * LdotH * LdotH * perceptualRoughness;
	half nlPow5 = Pow5(1 - NdotL);
	half nvPow5 = Pow5(1 - NdotV);
	return (1 + (fd90 - 1) * nlPow5) * (1 + (fd90 - 1) * nvPow5) * NdotL;
}

/////////////////////////////////////ShaderInputBase/////////////////////////////////////////////////////////

uniform float4 _Color;
uniform sampler2D _MainTex; uniform float4 _MainTex_ST;
uniform sampler2D _BumpMap; uniform float4 _BumpMap_ST;
uniform sampler2D _Masks;
uniform float _NormalScale;
uniform float _MetallicScale;
uniform float _RoughnessScale;
uniform float _AOIntensity;
#if defined(_EMISSION)
uniform sampler2D _Emission;
uniform half4 _EmissionColor;
uniform half _EmissionScale;

#endif

uniform float _Cutoff;

struct VertexInput
{
	float4 vertex   : POSITION;
	half3 normal    : NORMAL;
	float2 texcoord0      : TEXCOORD0;
	float2 texcoord1      : TEXCOORD1;
#if defined(DYNAMICLIGHTMAP_ON) || defined(UNITY_PASS_META)
	float2 texcoord2      : TEXCOORD2;
#endif
	half4 tangent   : TANGENT;
	UNITY_VERTEX_INPUT_INSTANCE_ID
};

struct VertexOutputForwardBase {
	float4 pos : SV_POSITION;
	float4 uv0 : TEXCOORD0;
	float4 normalDir : TEXCOORD1;
	float4 tangentDir : TEXCOORD2;
	float4 bitangentDir : TEXCOORD3;
	UNITY_SHADOW_COORDS(4)
	UNITY_FOG_COORDS(5)
#if defined(LIGHTMAP_ON) || defined(UNITY_SHOULD_SAMPLE_SH)
	half4 ambientOrLightmapUV : TEXCOORD6;
#endif
	UNITY_VERTEX_INPUT_INSTANCE_ID
};

struct VertexOutputForwardAdd {
	float4 pos : SV_POSITION;
	float2 uv0 : TEXCOORD0;
	half4 normalDir : TEXCOORD1;
	half4 tangentDir : TEXCOORD2;
	half4 bitangentDir : TEXCOORD3;
	UNITY_SHADOW_COORDS(4)
	UNITY_FOG_COORDS(5)
};

////////////////////////////////////////////////////////////////////////////////////
inline half4 QtzVertexGIForward(VertexInput v, float3 posWorld, half3 normalWorld)
{
    half4 ambientOrLightmapUV = 0;
    // Static lightmaps
    #ifdef LIGHTMAP_ON
        ambientOrLightmapUV.xy = v.texcoord1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
        ambientOrLightmapUV.zw = 0;
    // Sample light probe for Dynamic objects only (no static or dynamic lightmaps)
    #elif UNITY_SHOULD_SAMPLE_SH
        #ifdef VERTEXLIGHT_ON
			#if defined(_CUSTOM_ENV_ON) || defined(_CUSTOM_ENV_VIEW_ON)
				half theta = (270 - qtz_SpecRotation) * UNITY_PI / 180.0f;
				half costha = cos(theta);
				half sintha = sin(theta);
				normalWorld = half3(normalWorld.x * costha - normalWorld.z * sintha, normalWorld.y, normalWorld.x * sintha + normalWorld.z * costha);
				ambientOrLightmapUV.rgb = QtzShadeSHPerVertex(normalWorld, ambientOrLightmapUV.rgb);
			#else
				// Approximated illumination from non-important point lights
				ambientOrLightmapUV.rgb = Shade4PointLights (
					unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,
					unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,
					unity_4LightAtten0, posWorld, normalWorld);
			#endif
        #endif

        ambientOrLightmapUV.rgb = QtzShadeSHPerVertex(normalWorld, ambientOrLightmapUV.rgb);
    #endif

    #ifdef DYNAMICLIGHTMAP_ON
        ambientOrLightmapUV.zw = v.texcoord2.xy * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;
    #endif

    return ambientOrLightmapUV;
}

/////////////////////////////////////ShadowCaster/////////////////////////////////////////////////////////
struct VertexInputShadowCaster
{
	float4 vertex	: POSITION;
	float3 normal	: NORMAL;
	float2 uv0		: TEXCOORD0;
};

struct VertexOutputShadowCaster
{
	float3 vec : TEXCOORD0;
	float2 tex : TEXCOORD1;
};

void vertShadowCaster(VertexInputShadowCaster v, out VertexOutputShadowCaster o, out float4 opos : SV_POSITION)
{
	o = (VertexOutputShadowCaster)0;
	TRANSFER_SHADOW_CASTER_NOPOS(o, opos)
	o.tex = TRANSFORM_TEX(v.uv0, _MainTex);
}

half4 fragShadowCaster(VertexOutputShadowCaster i) : SV_Target
{
	half alpha = tex2D(_MainTex, i.tex).a * _Color.a;
#if defined(_ALPHATEST_ON) || defined(_ALPHABLEND_ON)
	clip(alpha - _Cutoff);
#endif
	SHADOW_CASTER_FRAGMENT(i)
}

/////////////////////////////////////MetaPass/////////////////////////////////////////////////////////
struct VertexInputMeta {
	float4 vertex : POSITION;
	float2 texcoord0 : TEXCOORD0;
	float2 texcoord1 : TEXCOORD1;
	float2 texcoord2 : TEXCOORD2;
};

struct VertexOutputMeta {
	float4 pos : SV_POSITION;
	float2 uv0 : TEXCOORD0;
};

#include "UnityMetaPass.cginc"

VertexOutputMeta vert_meta(VertexInputMeta v) {
	VertexOutputMeta o = (VertexOutputMeta)0;
	o.uv0 = TRANSFORM_TEX(v.texcoord0, _MainTex);
	o.pos = UnityMetaVertexPosition(v.vertex, v.texcoord1.xy, v.texcoord2.xy, unity_LightmapST, unity_DynamicLightmapST);
	return o;
}

float4 frag_meta(VertexOutputMeta i) : SV_Target{
	float4 _MainTex_var = tex2D(_MainTex, i.uv0);
#if defined(_ALPHATEST_ON)
	clip(_MainTex_var.a - _Cutoff);
#endif
	UnityMetaInput o;
	UNITY_INITIALIZE_OUTPUT(UnityMetaInput, o);
	float3 diffuseColor = (_MainTex_var.rgb * _Color.rgb);
	float specularMonochrome;
	float3 specColor;
	float4 _Masks_var = tex2D(_Masks, i.uv0);
	diffuseColor = DiffuseAndSpecularFromMetallic(diffuseColor, _Masks_var.r, specColor, specularMonochrome);
	float roughness = saturate(_Masks_var.g * _RoughnessScale);
#if defined(EDITOR_VISUALIZATION)
	o.Albedo = diffuseColor;
#else
	o.Albedo = diffuseColor + specColor * roughness * 0.5;
#endif
#if defined(_EMISSION)
	o.Emission = tex2D(_Emission, i.uv0).rgb * _EmissionColor.rgb * _EmissionScale;
#endif
	o.SpecularColor = specColor;
	return UnityMetaFragment(o);
}

//////////////////////////////////////////Detail//////////////////////////////////////////////
uniform sampler2D _DetailMask;
uniform float4 _Color1;
uniform float4 _Param1;//(Intensity,Metallic,Roughness)
uniform float4 _Color2;
uniform float4 _Param2;//(Intensity,Metallic,Roughness)
uniform float4 _Color3;
uniform float4 _Param3;//(Intensity,Metallic,Roughness)
uniform float4 _Color4;
uniform float4 _Param4;//(Intensity,Metallic,Roughness)

uniform sampler2D _DetailTex1;
uniform float4 _DetailColor1;
uniform float4 _DetailScaleOffset1;
uniform float4 _DetailParam1; //(Intensity,Metallic,Roughness)

uniform sampler2D _DetailTex2;
uniform float4 _DetailColor2;
uniform float4 _DetailScaleOffset2;
uniform float4 _DetailParam2; //(Intensity,Metallic,Roughness)

uniform sampler2D _DetailTex3;
uniform float4 _DetailColor3;
uniform float4 _DetailScaleOffset3;
uniform float4 _DetailParam3; //(Intensity,Metallic,Roughness)

uniform sampler2D _DetailTex4;
uniform float4 _DetailColor4;
uniform float4 _DetailScaleOffset4;
uniform float4 _DetailParam4; //(Intensity,Metallic,Roughness)

inline void DetailMask(half2 uv, inout half4 color, inout half metallic, inout half gloss)
{
	fixed4 _DetailMask_var = tex2D(_DetailMask, uv);

	half4 _NewColor1 = /*_DetailMask_var.r * */_Param1.r * _Color1;
	half4 _NewColor2 = /*_DetailMask_var.g * */_Param2.r * _Color2;
	half4 _NewColor3 = /*_DetailMask_var.b * */_Param3.r * _Color3;
	half4 _NewColor4 = /*_DetailMask_var.a * */_Param4.r * _Color4;

	half _NewMetallic1 = /*_DetailMask_var.r * */_Param1.g;
	half _NewMetallic2 = /*_DetailMask_var.g * */_Param2.g;
	half _NewMetallic3 = /*_DetailMask_var.b * */_Param3.g;
	half _NewMetallic4 = /*_DetailMask_var.a * */_Param4.g;

	half _NewGloss1 = /*_DetailMask_var.r * */_Param1.b;
	half _NewGloss2 = /*_DetailMask_var.g * */_Param2.b;
	half _NewGloss3 = /*_DetailMask_var.b * */_Param3.b;
	half _NewGloss4 = /*_DetailMask_var.a * */_Param4.b;

	fixed4 _DetailTex1_var = tex2D(_DetailTex1, uv.xy * _DetailScaleOffset1.xy + _DetailScaleOffset1.zw);
	fixed4 _DetailTex2_var = tex2D(_DetailTex2, uv.xy * _DetailScaleOffset2.xy + _DetailScaleOffset2.zw);
	fixed4 _DetailTex3_var = tex2D(_DetailTex3, uv.xy * _DetailScaleOffset3.xy + _DetailScaleOffset3.zw);
	fixed4 _DetailTex4_var = tex2D(_DetailTex4, uv.xy * _DetailScaleOffset4.xy + _DetailScaleOffset4.zw);

	half4 _NewDetailColor1 = /*_DetailMask_var.r * */_DetailParam1.r * _DetailTex1_var * _DetailColor1;
	half4 _NewDetailColor2 = /*_DetailMask_var.g * */_DetailParam2.r * _DetailTex2_var * _DetailColor2;
	half4 _NewDetailColor3 = /*_DetailMask_var.b * */_DetailParam3.r * _DetailTex3_var * _DetailColor3;
	half4 _NewDetailColor4 = /*_DetailMask_var.a * */_DetailParam4.r * _DetailTex4_var * _DetailColor4;

	half gray1 = (_DetailTex1_var.r + _DetailTex1_var.g + _DetailTex1_var.b) * 0.33333333f;
	half gray2 = (_DetailTex2_var.r + _DetailTex2_var.g + _DetailTex2_var.b) * 0.33333333f;
	half gray3 = (_DetailTex3_var.r + _DetailTex3_var.g + _DetailTex3_var.b) * 0.33333333f;
	half gray4 = (_DetailTex4_var.r + _DetailTex4_var.g + _DetailTex4_var.b) * 0.33333333f;

	half _NewDetailMetallic1 = /*_DetailMask_var.r * */_DetailParam1.g;
	half _NewDetailMetallic2 = /*_DetailMask_var.g * */_DetailParam2.g;
	half _NewDetailMetallic3 = /*_DetailMask_var.b * */_DetailParam3.g;
	half _NewDetailMetallic4 = /*_DetailMask_var.a * */_DetailParam4.g;

	half _NewDetailGloss1 = /*_DetailMask_var.r * */_DetailParam1.b;
	half _NewDetailGloss2 = /*_DetailMask_var.g * */_DetailParam2.b;
	half _NewDetailGloss3 = /*_DetailMask_var.b * */_DetailParam3.b;
	half _NewDetailGloss4 = /*_DetailMask_var.a * */_DetailParam4.b;

	/*
	metallic = lerp(metallic, _NewMetallic1 * _NewDetailMetallic1, _DetailMask_var.r);
	metallic = lerp(metallic, _NewMetallic2 * _NewDetailMetallic2, _DetailMask_var.g);
	metallic = lerp(metallic, _NewMetallic3 * _NewDetailMetallic3, _DetailMask_var.b);
	metallic = lerp(metallic, _NewMetallic4 * _NewDetailMetallic4, _DetailMask_var.a);

	gloss = lerp(gloss, _NewGloss1 * _NewDetailGloss1, _DetailMask_var.r);
	gloss = lerp(gloss, _NewGloss2 * _NewDetailGloss2, _DetailMask_var.g);
	gloss = lerp(gloss, _NewGloss3 * _NewDetailGloss3, _DetailMask_var.b);
	gloss = lerp(gloss, _NewGloss4 * _NewDetailGloss4, _DetailMask_var.a);
	*/

	color = lerp(color, _NewColor1 + _NewDetailColor1, _DetailMask_var.r);
	color = lerp(color, _NewColor2 + _NewDetailColor2, _DetailMask_var.g);
	color = lerp(color, _NewColor3 + _NewDetailColor3, _DetailMask_var.b);
	color = lerp(color, _NewColor4 + _NewDetailColor4, _DetailMask_var.a);

	metallic = lerp(metallic, lerp(_NewMetallic1 * metallic, _NewDetailMetallic1, gray1), _DetailMask_var.r);
	metallic = lerp(metallic, lerp(_NewMetallic2 * metallic, _NewDetailMetallic2, gray2), _DetailMask_var.g);
	metallic = lerp(metallic, lerp(_NewMetallic3 * metallic, _NewDetailMetallic3, gray3), _DetailMask_var.b);
	metallic = lerp(metallic, lerp(_NewMetallic4 * metallic, _NewDetailMetallic4, gray4), _DetailMask_var.a);

	gloss = lerp(gloss, lerp(_NewGloss1 * gloss, _NewDetailGloss1, gray1), _DetailMask_var.r);
	gloss = lerp(gloss, lerp(_NewGloss2 * gloss, _NewDetailGloss2, gray2), _DetailMask_var.g);
	gloss = lerp(gloss, lerp(_NewGloss3 * gloss, _NewDetailGloss3, gray3), _DetailMask_var.b);
	gloss = lerp(gloss, lerp(_NewGloss4 * gloss, _NewDetailGloss4, gray4), _DetailMask_var.a);

	//_Color *= (_NewColor1 + _NewColor2 + _NewColor3 + _NewColor4) * (_NewDetailColor1 + _NewDetailColor2 + _NewDetailColor3 + _NewDetailColor4);
	//metallic *= saturate(_NewMetallic);
}

//////////////////////////////////////////Weather//////////////////////////////////////////////
uniform float _EnableWeather;
uniform sampler2D _RainMap;
uniform float4 _RainMap_ST;
uniform float4 _RainParam;
uniform float _WetStrength;
uniform float _SnowStrength;
uniform float4 _SnowColor;

inline half3 ComputeRipple(half4 Ripple, half CurrentTime, half Weight)
{
	Ripple.yz = Ripple.yz * 2 - 1; // Decompress perturbation

	half DropFrac = frac(Ripple.w + CurrentTime); // Apply time shift
	half TimeFrac = DropFrac - 1.0f + Ripple.x;
	half DropFactor = saturate(0.2f + Weight * 0.8f - DropFrac);
	half FinalFactor = DropFactor * Ripple.x *
		sin(clamp(TimeFrac * 9.0f, 0.0f, 3.0f) * UNITY_PI);

	return half3(Ripple.yz * FinalFactor * 0.35f, 1.0f);
}

inline void WeatherEffect(half2 uv, inout fixed3 bumpmap)
{
	half4 TimeMul = float4(1.0f, 0.85f, 0.93f, 1.13f);
	half4 TimeAdd = float4(0.0f, 0.2f, 0.45f, 0.7f);
	half4 Times = (_Time.y * _RainParam.y * TimeMul + TimeAdd) * 1.6f;
	Times = frac(Times);

	half2 coord = uv * _RainMap_ST.xy + _RainMap_ST.zw;

	half3 Ripple1 = ComputeRipple(tex2D(_RainMap, coord + half2(0.25f, 0.0f)), Times.x, 3);
	half3 Ripple2 = ComputeRipple(tex2D(_RainMap, coord + half2(-0.55f, 0.3f)), Times.y, 3);
	half3 Ripple3 = ComputeRipple(tex2D(_RainMap, coord + half2(0.6f, 0.85f)), Times.z, 3);
	half3 Ripple4 = ComputeRipple(tex2D(_RainMap, coord + half2(0.5f, -0.75f)), Times.w, 3);

	half4 Weights = _RainParam.x - half4(0, 0.25, 0.5, 0.75);
	Weights = saturate(Weights * 4);
	// Compose normal of the four layer based on weights
	half4 Z = lerp(1, half4(Ripple1.z, Ripple2.z, Ripple3.z, Ripple4.z), Weights);
	half3 Normal = half3(Weights.x * Ripple1.xy +
		Weights.y * Ripple2.xy +
		Weights.z * Ripple3.xy +
		Weights.w * Ripple4.xy,
		Z.x * Z.y * Z.z * Z.w);
	// return result                             
	bumpmap = lerp(normalize(bumpmap + Normal), Normal, _RainParam.z);
}

uniform fixed4 _ShadowColor;



uniform half _ToonDiffuseClip;
uniform half3 _ToonDarkColor;
uniform half _ToonGloss;
uniform half _ToonSpecularClip;
uniform half _ToonLightIntensity;
uniform half3 _ToonSpecluarColor;

inline half3 ToonDiffuse(half NdotL, half diffuseClip, half3 diffuseColor, half3 darkColor, half3 lightColor)
{
	half toon_light = step(diffuseClip, NdotL);
	return diffuseColor * lightColor * toon_light + darkColor * diffuseColor * (1 - toon_light);
}

inline half3 ToonSpecluar(half NdotH, half gloss, half specularClip, half lightIntensity, half3 specularColor, half3 lightColor)
{
	half toon_light = max(1 - pow(NdotH, gloss * 128.0f), 0.0f);
	return step(toon_light, specularClip) * lightIntensity * specularColor * lightColor;
}


inline half3x3 ViewDirectionToWorldMatrix(half3 worldPos)
{
	half3 forward = normalize(worldPos - _WorldSpaceCameraPos);
	half3 up = UNITY_MATRIX_V[1].xyz;
	half3 right = cross(forward, up);
	up = cross(right, forward);
	return half3x3(right, up, -forward);
}

#endif //QTZ_COMMON_INCLUDED